﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using Common;

namespace ServiceBusLibrary
{
    [ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
    public class ServiceBus : IServiceBus
    {
        NLog.Logger logger = null;

        public ServiceBus()
        {
            logger = NLog.LogManager.GetCurrentClassLogger();
            Logger.type = "service_bus";
        }

        public Response ProcessRequest(Request request)
        {
            Response response = new Response();
            response.Action = request.Action;
            response.ServiceID = request.ServiceID;
            try
            {
                switch (request.ServiceID)
                {
                    case 0: response = CallCoreService(request); break;
                    case 1: response = CallAuthorService(request); break;
                    case 2: response = CallEditorService(request); break;
                    case 3: response = CallReviewerService(request); break;
                    case 4: response = CallPublishingEditorService(request); break;
                    default: break;
                }
            }
            catch (FaultException fe)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", fe);
                response.Error = new Error()
                {
                    TargetSite = fe.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = fe.Message
                };
            }
            catch (CommunicationException ce)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", ce);
                response.Error = new Error()
                {
                    TargetSite = ce.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ce.Message
                };
            }
            catch (TimeoutException te)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", te);                
                response.Error = new Error()
                {
                    TargetSite = te.TargetSite.Name,
                    Type = ErrorTypes.OperationTimedOut,
                    Details = te.Message
                };
            }
            catch (Exception ex)
            {
                logger.ErrorException("Exeption in " + this.GetType().Name + "." + MethodBase.GetCurrentMethod().Name + " method", ex);
                response.Error = new Error()
                {
                    TargetSite = ex.TargetSite.Name,
                    Type = ErrorTypes.InternalError,
                    Details = ex.Message
                };
            }
            return response;
        }

        private Response CallPublishingEditorService(Request request)
        {
            PublishingEditorService.PublishingEditorServiceClient client = new PublishingEditorService.PublishingEditorServiceClient();
            return InvokeClient(client, request.Params, request.Action);
        }

        private Response CallReviewerService(Request request)
        {
            ReviewerServiceReference.ReviewerServiceClient client = new ReviewerServiceReference.ReviewerServiceClient();
            return InvokeClient(client, request.Params, request.Action);
        }

        private Response CallEditorService(Request request)
        {
            EditorServiceReference.EditorServiceClient client = new EditorServiceReference.EditorServiceClient();
            return InvokeClient(client, request.Params, request.Action);
        }

        private Response CallCoreService(Request request)
        {
            CoreServiceReference.CoreServiceClient client = new CoreServiceReference.CoreServiceClient();
            return InvokeClient(client, request.Params, request.Action);
        }

        private Response CallAuthorService(Request request)
        {
            AuthorServiceReference.AuthorServiceClient client = new AuthorServiceReference.AuthorServiceClient();
            return InvokeClient(client, request.Params, request.Action);
        }

        private Response InvokeClient(ICommunicationObject client, Dictionary<string,object> values, string action)
        {
            Response response = new Response();
            var method = client.GetType().GetMethod(action);
            if (method == null)
            {
                logger.Error("Method " + action + " was not found on the service");
                response.Error = new Error()
                {
                    Details = "Requested paramater 'Action' (" + action + ") was not found on the service. Please fix it and try again.",
                    TargetSite = MethodBase.GetCurrentMethod().Name,
                    Type = ErrorTypes.InvalidParamater
                };
            }
            else
            {
                var parameters = method.GetParameters();
                var parameterList = new List<object>();

                foreach (var parameter in parameters)
                {
                    var elem = (from v in values where v.Key == parameter.Name select v.Value).SingleOrDefault();
                    if (elem != null)
                    {
                        parameterList.Add(elem);
                    }
                }
                // Check for correct request input
                if (parameters.Length != 0 && parameterList.Count == 0)
                {
                    throw new ArgumentException("No parametres found in the request. Please try again");
                }
                response = (Response)method.Invoke(client, parameterList.ToArray());
            }

            try
            {
                client.Close();
            }
            catch
            {
                client.Abort();
            }
            return response;
        }
    }
}